#!/usr/bin/env python
# $Id: pypi.py df54dec975f4 2012/09/09 20:22:40 pgurumur $
# Copyright (c) 2012 Prabhu Gurumurthy <pgurumur@gmail.com>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#

__version__  = "$Revision: df54dec975f4 $"
__author__   = "$Author: pgurumur $"
__modified__ = "$Date: 2012-09-09 20:22:40Z $"

import urllib2
import optparse
import sys
import StringIO
import time

from venya.lib import cmdparser
from venya.lib import sql
from venya.tools import dbpi
from venya.tools import linkparser

class pypi(cmdparser.GetOpt):
   option_list = cmdparser.GetOpt.option_list + (
         optparse.make_option("-u", "--url", type = "str",
            help = "PyPI URL, def: http://pypi.python.org/pypi?%3Aaction=index",
            default = "http://pypi.python.org/pypi?%3Aaction=index",
            dest = "url"),
         optparse.make_option("-d", "--db", type = "str",
            help = "SQLite database, def: pypi.db", default = "/etc/pypi.db",
            dest = "db"),
         optparse.make_option("-U", "--update", action = "store_true",
            help = "Update package information onto database", default = False,
            dest = "update"),
         optparse.make_option("-s", "--search", type = "str",
            help = "Search package information in database", dest = "search"),
         )

   def __init__(self, **kwargs):
      super(pypi, self).__init__(**kwargs)
      self._url = None
      self._content = StringIO.StringIO()
      self._sql = None
      self._oneday = 86400

   def handle(self):
      (options, args) = self.parse()
      starttime = time.time()
      update = False

      try:
         if options.db:
            self._sql = sql.sqlite(options.db, bool(options.verbosity))
            dbpi.create(self._sql.engine)
         else:
            self.error("No database defined!")
      except sql.SQLError, message:
         self.error(message)
      except dbpi.ModelError, message:
         self.error(message)

      piq = None
      puq = None
      for value in self._sql.inspect:
         if str(value) == "pypi_info":
            piq = dbpi.QueryObject(self._sql.query(value), value)
         elif str(value) == "pypi_dbupdate":
            puq = dbpi.QueryObject(self._sql.query(value), value)

      if not options.update:
         if (len(puq.query.all()) <= 0) or (len(piq.query.all()) <= 0):
            update = True
         elif len(puq.query.all()) > 1:
            self.error("Too many updated entries, remove them manually!")
         else:
            (pk, timeval) = puq.query.filter(puq.table.c.lastupdate).all()[0]
            if (starttime - float(timeval)) > self._oneday:
               update = True
      else:
         update = True

      if update:
         try:
            if options.url:
               self._url = urllib2.urlopen(options.url)
            else:
               self.error("no URL defined!")
         except urllib2.URLError, message:
            self.error(message)
         except urllib2.HTTPError, message:
            self.error(message)
   
         self._content.write(self._url.read())
         self._content.seek(0)
         self._pypi = linkparser.PyLinkParser()
         self._pypi.feed(self._content.read())
         if len(self._pypi):
            for (package, version) in self._pypi.links:
               query = piq.query.filter(piq.table.c.package == package)
               count = query.count()
               if count:
                  [(pkgver, pk) for (pkgver, pk) in query.values(
                        piq.table.c.version, piq.table.c.pk)]
                  if pkgver != version:
                     self._sql.execute(piq.table.update().where(
                        piq.table.c.pk == pk).values(version = version))
               else:
                  table = dbpi.PyPackageInfo(package, version)
                  self._sql.add(table, commit = True)

            self._sql.execute(
                  puq.table.update().where(puq.table.c.pk == 2).values(
                     lastupdate = starttime))

      if options.search:
         searchstr = "%%%s%%" %options.search
         for (pkg, version) in piq.query.filter(
               piq.table.c.package.like(searchstr)).values(
                     piq.table.c.package, piq.table.c.version):
            print pkg, version

if __name__ == "__main__":
   pypiopt = None
   try:
      pypiopt = pypi(version = 1.0, args = sys.argv)
      if len(pypiopt) <= 1:
         pypiopt.printhelp()
      else:
         pypiopt.handle()
   except cmdparser.GetOptError, message:
      print message
      sys.exit(1)
